package fr.lip6.meta.ple.generation;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
//import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
//import java.util.List;

import org.eclipse.gmt.modisco.java.ConstructorDeclaration;
import org.eclipse.gmt.modisco.java.MethodDeclaration;

//import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;

//import artefact.generic.ConstructionPrimitive;
import artefact.umlClassDiagram.CreateAttribute;
import artefact.umlClassDiagram.CreateGeneralization;
import artefact.umlClassDiagram.CreateOperation;
import artefact.umlClassDiagram.impl.UmlClassDiagramFactoryImpl;
import fr.lip6.meta.ple.constraintsextraction.constraint.Constraint;
import fr.lip6.meta.ple.featureIdentification.Feature;
import fr.lip6.meta.ple.generation.aspectj.ProductLineConstructionAJ;
import fr.lip6.meta.ple.generation.fh.java.GenerateFromXmiFHJava;
import fr.lip6.meta.ple.generation.fh.java.ReadXmiFHJava;
import fr.lip6.meta.ple.generation.strategy.GenerationContext;
import fr.lip6.meta.ple.generation.strategy.ReadXmiStrategy;
import fr.lip6.meta.ple.plextraction.Extract;
import fr.lip6.meta.ple.plextraction.config.Configuration;
import fr.lip6.meta.tools.StatementPrimitive;
import fr.lip6.meta.tools.Trigger;

public class CompareMethode {
	UmlClassDiagramFactoryImpl factory = new UmlClassDiagramFactoryImpl();
	ArrayList<Constraint> constraints = new ArrayList<Constraint>();
	private GenerationContext genContext;

	public CompareMethode() {
		this.genContext = Configuration.getInstance().getGenerationContext();

		if (genContext == null) {
			throw new RuntimeException("Please set up generation context into configuration.");
		}
	}

	
	/**
	 * Description ?
	 * @param features
	 * @param cons
	 * @return
	 */
	public ArrayList<Feature> extract(ArrayList<Feature> features, ArrayList<Constraint> cons) {
		ArrayList<CreateGeneralization> allCreateGeneralizations;
		ArrayList<Feature> featurescopy1;
		ArrayList<Feature> subfeatures;

		constraints.addAll(cons);
		allCreateGeneralizations = getAllCreateGeneralizationsInCollectionOfFeature(features);
		featurescopy1 = (ArrayList<Feature>) features.clone();
		subfeatures = new ArrayList<Feature>();

		for (Iterator<Feature> iter1 = features.iterator(); iter1.hasNext();	) {
			Feature feat = iter1.next();
			int j = 0;
			int k = 0;

			for(Iterator<Trigger> iter = feat.iterator(); iter.hasNext();	) {
				Trigger t = iter.next();
				StatementPrimitive st = (StatementPrimitive)t;

				if (st.getPrimitive() instanceof CreateOperation) {
					CreateOperation crOp = (CreateOperation) st.getPrimitive();

					if(! isGenralizationOperation(crOp, allCreateGeneralizations)) {
						LinkedHashSet<LinkedHashSet<Integer>> list;

						if(!(crOp.getName().equals(crOp.getOwener()))) {
							list = processoperation(feat, crOp, true) ;
						} else {
							list = processoperation(feat, crOp, false) ;  
						}

						LinkedHashSet<LinkedHashSet<Integer>> list1 = (LinkedHashSet<LinkedHashSet<Integer>>) list.clone();

						if(list1.size() > 1) {
							boolean delete = true;

							System.err.println("method name is "+crOp.getName());
							//StatementPrimitive st1 = st;

							// Iterator<LinkedHashSet<LinkedHashSet<Integer>>> ps = list.iterator();
							System.err.println(crOp.getName() + crOp.getOwener()+" size = "+list1.size());
							int s = 0;
							for(LinkedHashSet<Integer> ps : list1) {
								Iterator<Integer> ips = ps.iterator();
								int pint = ips.next().intValue();
								CreateOperation crCrOp = factory.createCreateOperation();
								ArrayList<String> operationVariables;
								Feature newf;

								crCrOp.setName(crOp.getName());
								crCrOp.setOwener(crOp.getOwener());
								crCrOp.setLien(crOp.getLien());

								//
								// Gestion of Java extension output method (FeatureHouse, AspectJ, ...)
								// -->
								GenerationContext genContext =	Configuration
										.getInstance()
										.getGenerationContext();

								switch(Configuration.getInstance().getExtensionType()) {
								// AspectJ
								case AJ:
									if (genContext == null)
										throw new RuntimeException("Please set up generation context for AJ into configuration.");
									else
										operationVariables = genContext.getOperationVariables(crCrOp);

									System.err.println( "variables in method "+crOp.getName()+" are "+  operationVariables.toString());

									newf = createSubFeature(crCrOp, pint, feat, j, k);

									// character '_' not permitted because of its package signification
									newf.setName(
											ProductLineConstructionAJ.transformFeatureName(crOp.getName())
											+ "V" + s
									);
									newf.getProdIds().addAll(ps);
									s++;

									break;
									// FeatureHouse
								default:
									if (genContext == null)
										throw new RuntimeException("Please set up generation context for FH into configuration.");
									else
										operationVariables = genContext.getOperationVariables(crCrOp);

									System.err.println( "variables in method "+crOp.getName()+" are "+  operationVariables.toString());

									newf = createSubFeature(crCrOp, pint, feat, j, k);

									newf.setName(crOp.getName()+"_v"+s);
									newf.getProdIds().addAll(ps);
									s++;

									break;
								}
								// <--
								// Gestion of Java extension output method (FeatureHouse, AspectJ, ...)
								//

								ArrayList<Feature> fs = getFeatures(operationVariables, featurescopy1, feat);

								for (Iterator<Feature> iter22 = fs.iterator(); iter22.hasNext();	) {
									Feature ff = iter22.next();
									Constraint c11 = new Constraint(newf, ff);
									constraints.add(c11);
								}

								if(delete) {
									iter.remove();
									Feature fsub = new Feature();
									fsub.setId(feat.getId()+"_"+k);
									fsub.setName(crOp.getName());

									subfeatures.add(fsub);

									delete = false;
								}

								subfeatures.add(newf);

								j++;
							}
							k++; 
						}
					}	
				}
			}
		}
		
		features.addAll(subfeatures);
		
		return features;
	}

	/**
	 * 
	 * @param f
	 * @param crOp
	 * @return
	 */

	public Feature createSubFeature(CreateOperation crOp, int p, Feature f, int j, int k) {
		Feature newf = new Feature();
		newf.setId(f.getId()+"_"+k+"_"+j);
		newf.setName(crOp.getName()+"v"+j);
		StatementPrimitive st = new StatementPrimitive(crOp);
		st.getPrimitive().setId(new BigInteger(""+p));
		String [] liens = crOp.getLien().split("::");
		String lien = "generated/"+Extract.nameTestCase+"/"+Extract.products[p]+"_java.xmi::"+liens[1]+"::"+liens[2];
		st.getPrimitive().setLien(lien);

		newf.add(st);
		for(Trigger t : newf) {
			StatementPrimitive st1 = (StatementPrimitive) t;
			System.err.println(st1.toString());
		}

		return (Feature) newf.clone();

	}
	public LinkedHashSet<LinkedHashSet<Integer>> processoperation(Feature f, CreateOperation o, boolean methode_or_constructor)
	{
		LinkedHashSet<LinkedHashSet<Integer>> list = new LinkedHashSet<LinkedHashSet<Integer>>();

		LinkedHashSet<Integer> proudectFeatureids = (LinkedHashSet<Integer>) f.getProdIds().clone();


		while(!proudectFeatureids.isEmpty())
		{

			LinkedHashSet<Integer> proudects = getProductWithSameCode(o, proudectFeatureids, methode_or_constructor);
			proudectFeatureids.removeAll(proudects);
			list.add(proudects);


		}


		return list;
	}







	/**
	 * 
	 * @param crOp
	 * @param productsids
	 * @return
	 */
	public LinkedHashSet<Integer> getProductWithSameCode(CreateOperation crOp, LinkedHashSet<Integer> productsids, boolean methode_or_constructor) {
		LinkedHashSet<Integer> res = new LinkedHashSet<Integer>();
		Iterator<Integer> products = productsids.iterator();
		int p0 = products.next().intValue();
		String fileName = "generated/"+Extract.nameTestCase+"/"+Extract.products[p0]+"_java.xmi";
		String [] lien = crOp.getLien().split("::");
		String params = lien.length > 2 ? lien[2] : null;
		String[] paramsArray = params == null ? new String[0] : params.substring(1, params.length()-1).split(",");
		if (paramsArray.length == 1 && paramsArray[0].equals("")) paramsArray = new String[0];
		
		res.add(new Integer(p0));
		
		if(methode_or_constructor) {
			MethodDeclaration md = GenerateFromXmiFHJava.getMethodDeclaration(fileName, lien[1], crOp.getOwener(), crOp.getName(), paramsArray);
			
			while(products.hasNext()) {
				int p = products.next().intValue();
				String fileName1 = "generated/"+Extract.nameTestCase+"/"+Extract.products[p]+"_java.xmi";
				String [] lien1 = crOp.getLien().split("::");
				String params1 = lien1.length > 2 ? lien1[2] : null;
				String[] paramsArray1 = params1 == null ? new String[0] : params1.substring(1, params1.length()-1).split(",");
				if (paramsArray1.length == 1 && paramsArray1[0].equals("")) paramsArray1 = new String[0];
				MethodDeclaration md1 = GenerateFromXmiFHJava.getMethodDeclaration(fileName1, lien1[1], crOp.getOwener(), crOp.getName(), paramsArray1);

				if(compareMethod(md1, md)) {
					res.add(new Integer(p));
				}
			}
		} else {
			ConstructorDeclaration cons = GenerateFromXmiFHJava.getConstructorDeclaration(fileName, lien[1], crOp.getOwener(), crOp.getName(), paramsArray);
			
			while(products.hasNext()) {
				int p = products.next().intValue();
				String fileName1 = "generated/"+Extract.nameTestCase+"/"+Extract.products[p]+"_java.xmi";
				String [] lien1 = crOp.getLien().split("::");
				String params1 = lien1.length > 2 ? lien1[2] : null;
				String[] paramsArray1 = params1 == null ? new String[0] : params1.substring(1, params1.length()-1).split(",");
				if (paramsArray1.length == 1 && paramsArray1[0].equals("")) paramsArray1 = new String[0];
				ConstructorDeclaration cons1 = GenerateFromXmiFHJava.getConstructorDeclaration(fileName1, lien1[1], crOp.getOwener(), crOp.getName(), paramsArray1);

				if(compareConstructor(cons, cons1)) {
					res.add(new Integer(p));
				}
			}
		}
		
		return res;
	}

	/**
	 * 
	 * @param m1
	 * @param m2
	 * @return
	 */

	boolean compareMethod(MethodDeclaration m1, MethodDeclaration m2)
	{
		ReadXmiStrategy readXmi = this.genContext.getReadXmiStrategy();

		String m1declaration = readXmi.getMethodDeclarationText(m1);
		String m2declaration = readXmi.getMethodDeclarationText(m2);

		if(!(m1declaration.equals(m2declaration))) {
			return false;
		} else {
			String m1body = readXmi.getMethodDeclarationAndBodyText(m1);
			String m2body = readXmi.getMethodDeclarationAndBodyText(m2);

			if (!(m1body.equals(m2body))) {
				return false;
			}
		}






		return true;
	}

	boolean compareConstructor(ConstructorDeclaration m1, ConstructorDeclaration m2) {
		ReadXmiStrategy readXmi = this.genContext.getReadXmiStrategy();
		String m1declaration = readXmi.getConstructorDeclarationText(m1);
		String m2declaration = readXmi.getConstructorDeclarationText(m2);

		if (!(m1declaration.equals(m2declaration))) {
			return false;
		} else {
			String m1body = readXmi.getConstructorDeclarationAndBodyText(m1);
			String m2body = readXmi.getConstructorDeclarationAndBodyText(m2);

			if(!(m1body.equals(m2body))) {
				return false;
			}
		}






		return true;
	}



	ArrayList<Feature> getFeatures(ArrayList<String> operatinVaribles, ArrayList<Feature> fs, Feature ff)
	{
		ArrayList<Feature> fs1 = new ArrayList<Feature>();
		for (Iterator<Feature> feature = fs.iterator(); feature.hasNext();)
		{
			Feature f = feature.next();
			if(!f.getId().equals(ff.getId()))
			{
				ArrayList<String> featureVaribles = getAllVaraibleofFeature(f);
				for(String s: featureVaribles )
				{
					if(operatinVaribles.contains(s))
					{
						fs1.add(f);break;
					}
				}
			}




		}
		return fs1;
	}





	public static ArrayList<String> getAllVaraibleofFeature(Feature f)
	{
		ArrayList<String> list = new ArrayList<String>();

		for (Iterator<Trigger> iter = f.iterator(); iter.hasNext();)
		{
			Trigger t = iter.next();

			StatementPrimitive st = (StatementPrimitive)t;

			if (st.getPrimitive() instanceof CreateAttribute)
			{
				CreateAttribute att = (CreateAttribute) st.getPrimitive();
				list.add(att.getName());
			}

		}
		return list;
	}

	public static ArrayList<CreateGeneralization> getAllCreateGeneralizationsInCollectionOfFeature(Collection<Feature> features) 
	{
		ArrayList<CreateGeneralization> allCreateGeneralization = new ArrayList<CreateGeneralization>();

		for(Feature f: features)
		{
			for(Trigger t: f)
			{
				StatementPrimitive st = (StatementPrimitive) t;
				if(st.getPrimitive() instanceof CreateGeneralization)
				{
					CreateGeneralization creatGeneralization = (CreateGeneralization) st.getPrimitive();


					allCreateGeneralization.add(creatGeneralization);
				}
			}
		}



		return allCreateGeneralization;
	}


	public static boolean isGenralizationOperation(CreateOperation o, ArrayList<CreateGeneralization> creategeberalizations)

	{
		boolean t = false;
		String operationClass = o.getOwener();
		for(CreateGeneralization g:creategeberalizations)
		{
			String subclass = g.getSub();
			if(subclass.equals(operationClass))
			{
				t = true;break;
			}

		}


		return t;
	}


	public ArrayList<Constraint> getConstraints()
	{
		return constraints;
	}

}
